home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 42 / Amiga Format AFCD42 (Issue 126, Aug 1999).iso / -serious- / programming / c / bitmap24 / bitmap24.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-05-17  |  12.5 KB  |  696 lines

  1. /* -------------------------------------------------------------------------- *\
  2.    BITMAP24.CPP, 24 bit bitmap handling, including IFF24 saving
  3.    Copyright (C) 1999  Jarno van der Linden
  4.    jarno@kcbbs.gen.nz
  5.  
  6.    This library is free software; you can redistribute it and/or
  7.    modify it under the terms of the GNU Library General Public
  8.    License as published by the Free Software Foundation; either
  9.    version 2 of the License, or (at your option) any later version.
  10.  
  11.    This library is distributed in the hope that it will be useful,
  12.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.    Library General Public License for more details.
  15.  
  16.    You should have received a copy of the GNU Library General Public
  17.    License along with this library; if not, write to the
  18.    Free Software Foundation, Inc., 59 Temple Place - Suite 330, Cambridge,
  19.    MA 02139, USA.
  20.  
  21.  
  22.    01 May 1999: Brought it all together in some sort of distributable form
  23. \* -------------------------------------------------------------------------- */
  24.  
  25. /* -------------------------------- Includes -------------------------------- */
  26. #include <dos/dos.h>
  27. #include <libraries/iffparse.h>
  28.  
  29. #include <proto/dos.h>
  30. #include <proto/iffparse.h>
  31.  
  32. #include <string.h>
  33. #include <stdio.h>
  34.  
  35. #include "bitmap24.h"
  36.  
  37. /* ------------------------------ Definitions ------------------------------- */
  38.  
  39. /* --------------------------------- Macros --------------------------------- */
  40. #define ID_ILBM    MAKE_ID('I','L','B','M')
  41. #define ID_BMHD    MAKE_ID('B','M','H','D')
  42. #define ID_BODY    MAKE_ID('B','O','D','Y')
  43.  
  44. /* -------------------------------- Typedefs -------------------------------- */
  45. typedef UBYTE Masking;
  46. #define mskNone    0
  47. typedef UBYTE Compression;
  48. #define cmpNone        0
  49. #define cmpByteRun    1
  50.  
  51. typedef struct {
  52.     UWORD w,h;
  53.     WORD x,y;
  54.     UBYTE nPlanes;
  55.     Masking masking;
  56.     Compression compression;
  57.     UBYTE reserved1;
  58.     UWORD transparentColor;
  59.     UBYTE xAspect,yAspect;
  60.     WORD pageWidth,pageHeight;
  61. } BitMapHeader;
  62.  
  63.  
  64. /* ------------------------------ Proto Types ------------------------------- */
  65.  
  66. /* -------------------------------- Structs --------------------------------- */
  67.  
  68. /* -------------------------------- Globals --------------------------------- */
  69.  
  70. /* ---------------------------------- Code ---------------------------------- */
  71. BitMap24::BitMap24()
  72. {
  73.     Constructor(0,0);
  74. }
  75.  
  76.  
  77. BitMap24::BitMap24(char *filename)
  78. {
  79.     Constructor(0,0);
  80.     if(!HasError())
  81.         ReadBitMap(filename);
  82. }
  83.  
  84.  
  85. void BitMap24::Constructor(UWORD width_arg, UWORD height_arg)
  86. {
  87.     error = BITMAP24_ERROR_NONE;
  88.     bitmap = NULL;
  89.  
  90.     if((width_arg != 0) && (height_arg != 0))
  91.         SetSize(width_arg, height_arg);
  92. }
  93.  
  94.  
  95. BitMap24::~BitMap24()
  96. {
  97.     if(bitmap)
  98.         delete[] bitmap;
  99.     bitmap = NULL;
  100. }
  101.  
  102.  
  103. int BitMap24::GetError()
  104. {
  105.     return error;
  106. }
  107.  
  108.  
  109. BOOL BitMap24::HasError()
  110. {
  111.     return error != BITMAP24_ERROR_NONE;
  112. }
  113.  
  114.  
  115. int BitMap24::ResetError()
  116. {
  117.     int olderr;
  118.  
  119.     olderr = error;
  120.     error = BITMAP24_ERROR_NONE;
  121.  
  122.     return olderr;
  123. }
  124.  
  125.  
  126. char *BitMap24::GetErrorStr(int err)
  127. {
  128.     switch(err)
  129.     {
  130.         case BITMAP24_ERROR_NONE:
  131.             return "No error";
  132.         case BITMAP24_ERROR_NOBITMAP:
  133.             return "BitMap couldn't be allocated";
  134.         case BITMAP24_ERROR_FILEOPEN:
  135.             return "Error opening output file";
  136.         case BITMAP24_ERROR_OUTOFBOUNDS:
  137.             return "Out of bounds coordinate used";
  138.         case BITMAP24_ERROR_ALLOCFAILURE:
  139.             return "Memory allocation failure";
  140.         case BITMAP24_ERROR_FILEFORMAT:
  141.             return "File format not what expected";
  142.         case BITMAP24_ERROR_PREPARSE:
  143.             return "Setting up IFF parsing failed";
  144.         case BITMAP24_ERROR_PARSE:
  145.             return "Error during parsing";
  146.     }
  147.  
  148.     return "Unknown error";
  149. }
  150.  
  151.  
  152. char *BitMap24::GetErrorStr()
  153. {
  154.     return GetErrorStr(error);
  155. }
  156.  
  157.  
  158. void BitMap24::SetSize(UWORD width_arg, UWORD height_arg)
  159. {
  160.     if(bitmap)
  161.         delete[] bitmap;
  162.     bitmap = NULL;
  163.  
  164.     realwidth = width = width_arg;
  165.     realheight = height = height_arg;
  166.  
  167.     width += (16-(width & 15)) & 15;
  168.     bytewidth = width >> 3;
  169.  
  170.     bitmap = new Colour[width*height];
  171.     if(bitmap)
  172.         memset(bitmap,0,width*height*sizeof(Colour));
  173.     else
  174.         error = BITMAP24_ERROR_NOBITMAP;
  175. }
  176.  
  177.  
  178. UWORD BitMap24::GetWidth()
  179. {
  180.     return GetWidthFast();
  181. }
  182.  
  183.  
  184. UWORD BitMap24::GetHeight()
  185. {
  186.     return GetHeightFast();
  187. }
  188.  
  189.  
  190. UWORD BitMap24::GetRealWidth()
  191. {
  192.     return GetRealWidthFast();
  193. }
  194.  
  195.  
  196. UWORD BitMap24::GetRealHeight()
  197. {
  198.     return GetRealHeightFast();
  199. }
  200.  
  201.  
  202. BOOL BitMap24::BoundsCheck(int x,int y)
  203. {
  204.     if((x < 0) || (x >= width) || (y < 0) || (y >= height))
  205.     {
  206.         error = BITMAP24_ERROR_OUTOFBOUNDS;
  207.         return FALSE;
  208.     }
  209.  
  210.     return TRUE;
  211. }
  212.  
  213.  
  214. void BitMap24::GetColour(Colour *c,int x,int y)
  215. {
  216.     if(BoundsCheck(x,y))
  217.         GetColourFast(c,x,y);
  218. }
  219.  
  220.  
  221. UBYTE BitMap24::GetRed(int x,int y)
  222. {
  223.     if(BoundsCheck(x,y))
  224.         return GetRedFast(x,y);
  225.  
  226.     return 0;
  227. }
  228.  
  229.  
  230. UBYTE BitMap24::GetGreen(int x,int y)
  231. {
  232.     if(BoundsCheck(x,y))
  233.         return GetGreenFast(x,y);
  234.  
  235.     return 0;
  236. }
  237.  
  238.  
  239. UBYTE BitMap24::GetBlue(int x,int y)
  240. {
  241.     if(BoundsCheck(x,y))
  242.         return GetBlueFast(x,y);
  243.  
  244.     return 0;
  245. }
  246.  
  247.  
  248. void BitMap24::SetColour(UBYTE r,UBYTE g,UBYTE b,int x,int y)
  249. {
  250.     if(BoundsCheck(x,y))
  251.         SetColourFast(r,g,b,x,y);
  252. }
  253.  
  254.  
  255. void BitMap24::SetColour(const Colour &c,int x,int y)
  256. {
  257.     if(BoundsCheck(x,y))
  258.         SetColourFast(c,x,y);
  259. }
  260.  
  261.  
  262. UBYTE BitMap24::GatherBits(int row, int c, int b, int x)
  263. {
  264.     UBYTE *bp;
  265.     UBYTE v;
  266.  
  267.     x <<= 3;
  268.     b = 1<<b;
  269.  
  270.     bp = ((UBYTE *)&bitmap[row*width+x]+c);
  271.  
  272.     v = (UBYTE)((*bp & b)!=0);
  273.     bp+=3;
  274.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  275.     bp+=3;
  276.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  277.     bp+=3;
  278.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  279.     bp+=3;
  280.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  281.     bp+=3;
  282.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  283.     bp+=3;
  284.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  285.     bp+=3;
  286.     v = (UBYTE)((v<<1) | ((*bp & b)!=0));
  287.  
  288.     return v;
  289. }
  290.  
  291.  
  292. void BitMap24::WriteChunkBytesCache(struct IFFHandle *iff, APTR data, LONG datasize)
  293. {
  294. #define CACHESIZE 8192
  295.     static BYTE datacache[CACHESIZE];
  296.     static LONG cachesize = 0;
  297.  
  298.     if(datasize == -1)
  299.     {
  300.         cachesize = 0;
  301.     }
  302.     else if(datasize == -2)
  303.     {
  304.         WriteChunkBytes(iff,datacache,cachesize);
  305.         cachesize = 0;
  306.     }
  307.     else
  308.     {
  309.         if((cachesize + datasize) >= CACHESIZE)
  310.         {
  311.             WriteChunkBytes(iff,datacache,cachesize);
  312.             WriteChunkBytes(iff,data,datasize);
  313.             cachesize = 0;
  314.         }
  315.         else
  316.         {
  317.             memcpy(datacache+cachesize,data,datasize);
  318.             cachesize += datasize;
  319.         }
  320.     }
  321. #undef CACHESIZE
  322. }
  323.  
  324.  
  325. void BitMap24::WriteRun(struct IFFHandle *iff, int row, int c, int b, int runstart, int runend)
  326. {
  327.     int x,n;
  328.     UBYTE v;
  329.     UBYTE data;
  330.  
  331.     v = GatherBits(row,c,b,runstart);
  332.  
  333.     for(x=runstart; x<=runend; x+=128)
  334.     {
  335.         n = (runend-x+1);
  336.         if(n > 128)
  337.             n = 128;
  338.  
  339.         data = (UBYTE)(-(n-1));
  340.         WriteChunkBytesCache(iff, &data, sizeof(data));
  341.         WriteChunkBytesCache(iff, &v, sizeof(v));
  342.     }
  343. }
  344.  
  345.  
  346. void BitMap24::WriteDump(struct IFFHandle *iff, int row, int c, int b, int runstart, int runend)
  347. {
  348.     int xx,x,n;
  349.     UBYTE data;
  350.  
  351.     for(x=runstart; x<=runend; x+=128)
  352.     {
  353.         n = (runend-x+1);
  354.         if(n > 128)
  355.             n = 128;
  356.  
  357.         data = (UBYTE)(n-1);
  358.         WriteChunkBytesCache(iff, &data, sizeof(data));
  359.         for(xx=0; xx<n; xx++)
  360.         {
  361.             data = GatherBits(row,c,b,xx+x);
  362.             WriteChunkBytesCache(iff, &data, sizeof(data));
  363.         }
  364.     }
  365. }
  366.  
  367.  
  368.  
  369. void BitMap24::FindRun(int row, int c, int b, int start, int *runstart, int *runlength)
  370. {
  371.     int x;
  372.     UBYTE vs,v;
  373.  
  374.     *runstart = x = start;
  375.     v = GatherBits(row,c,b,x);
  376.  
  377.     while(*runstart < bytewidth)
  378.     {
  379.         vs = v;
  380.         x++;
  381.         while((x < bytewidth) && ((v = GatherBits(row,c,b,x)) == vs))
  382.             x++;
  383.  
  384.         if((*runlength = x-*runstart) > 2)
  385.             return;
  386.  
  387.         *runstart = x;
  388.     }
  389. }
  390.  
  391.  
  392. void BitMap24::WriteBitMap(char *file)
  393. {
  394.     struct IFFHandle *iff;
  395.     BitMapHeader bmheader;
  396.     int y,c,b;
  397.     int runstart,runlength,runend;
  398.  
  399.     bmheader.w = realwidth;
  400.     bmheader.h = realheight;
  401.     bmheader.x = 0;
  402.     bmheader.y = 0;
  403.     bmheader.nPlanes = 24;
  404.     bmheader.masking = mskNone;
  405.     bmheader.compression = cmpByteRun;
  406.     bmheader.reserved1 = 0;
  407.     bmheader.transparentColor = 0;
  408.     bmheader.xAspect = 1;
  409.     bmheader.yAspect = 1;
  410.     bmheader.pageWidth = (WORD)realwidth;
  411.     bmheader.pageHeight = (WORD)realheight;
  412.  
  413.     iff = AllocIFF();
  414.     if(!iff)
  415.     {
  416.         error = BITMAP24_ERROR_ALLOCFAILURE;
  417.         return;
  418.     }
  419.  
  420.     iff->iff_Stream = Open(file,MODE_NEWFILE);
  421.     if(!(iff->iff_Stream))
  422.     {
  423.         error = BITMAP24_ERROR_FILEOPEN;
  424.         FreeIFF(iff);
  425.         return;
  426.     }
  427.  
  428.     InitIFFasDOS(iff);
  429.     if(OpenIFF(iff,IFFF_WRITE))
  430.     {
  431.         error = BITMAP24_ERROR_FILEOPEN;
  432.         Close(iff->iff_Stream);
  433.         FreeIFF(iff);
  434.         return;
  435.     }
  436.  
  437.     PushChunk(iff, ID_ILBM, ID_FORM, IFFSIZE_UNKNOWN);
  438.  
  439.     PushChunk(iff, ID_ILBM, ID_BMHD, sizeof(BitMapHeader));
  440.     WriteChunkBytes(iff,&bmheader,sizeof(bmheader));
  441.     PopChunk(iff);
  442.  
  443.     PushChunk(iff, ID_ILBM, ID_BODY, IFFSIZE_UNKNOWN);
  444.     WriteChunkBytesCache(iff,NULL,-1);
  445.     for(y=0; y<height; y++)
  446.     {
  447.         for(c=0; c<3; c++)
  448.         {
  449.             for(b=0; b<8; b++)
  450.             {
  451.                 runend = 0;
  452.  
  453.                 while(runend < bytewidth)
  454.                 {
  455.                     FindRun(y,c,b, runend, &runstart, &runlength);
  456.                     WriteDump(iff, y,c,b, runend,runstart-1);
  457.                     runend = runstart+runlength;
  458.                     if(runstart < bytewidth)
  459.                         WriteRun(iff, y,c,b, runstart,runend-1);
  460.                 }
  461.             }
  462.         }
  463.     }
  464.     WriteChunkBytesCache(iff,NULL,-2);
  465.     PopChunk(iff);
  466.  
  467.     PopChunk(iff);
  468.  
  469.     CloseIFF(iff);
  470.     Close(iff->iff_Stream);
  471.     FreeIFF(iff);
  472. }
  473.  
  474.  
  475. void BitMap24::SpreadBits(int row, int c, int b, int x, UBYTE v)
  476. {
  477.     UBYTE *bp;
  478.  
  479.     bp = ((UBYTE *)&bitmap[row*width+x]+c);
  480.     *bp |= ((v & 128) != 0) << b;
  481.     bp+=3;
  482.     *bp |= ((v & 64) != 0) << b;
  483.     bp+=3;
  484.     *bp |= ((v & 32) != 0) << b;
  485.     bp+=3;
  486.     *bp |= ((v & 16) != 0) << b;
  487.     bp+=3;
  488.     *bp |= ((v & 8) != 0) << b;
  489.     bp+=3;
  490.     *bp |= ((v & 4) != 0) << b;
  491.     bp+=3;
  492.     *bp |= ((v & 2) != 0) << b;
  493.     bp+=3;
  494.     *bp |= ((v & 1) != 0) << b;
  495. }
  496.  
  497.  
  498. struct IFFHandle *BitMap24::OpenIFFParse(char *file, ULONG chunk)
  499. {
  500.     struct IFFHandle *iff;
  501.  
  502.     if(iff = AllocIFF())
  503.     {
  504.         if(iff->iff_Stream= Open(file,MODE_OLDFILE))
  505.         {
  506.             InitIFFasDOS(iff);
  507.             if(!OpenIFF(iff,IFFF_READ))
  508.             {
  509.                 if(!StopChunk(iff,ID_ILBM, chunk))
  510.                 {
  511.                     if(!ParseIFF(iff,IFFPARSE_SCAN))
  512.                     {
  513.                         return iff;
  514.                     }
  515.                     if(!error) error = BITMAP24_ERROR_PARSE;
  516.                 }
  517.                 if(!error) error = BITMAP24_ERROR_PREPARSE;
  518.                 CloseIFF(iff);
  519.             }
  520.             if(!error) error = BITMAP24_ERROR_FILEOPEN;
  521.             Close(iff->iff_Stream);
  522.         }
  523.         if(!error) error = BITMAP24_ERROR_FILEOPEN;
  524.         FreeIFF(iff);
  525.     }
  526.     if(!error) error = BITMAP24_ERROR_ALLOCFAILURE;
  527.  
  528.     return NULL;
  529. }
  530.  
  531.  
  532. void BitMap24::CloseIFFParse(struct IFFHandle *iff)
  533. {
  534.     if(iff)
  535.     {
  536.         CloseIFF(iff);
  537.         if(iff->iff_Stream) Close(iff->iff_Stream);
  538.         FreeIFF(iff);
  539.     }
  540. }
  541.  
  542.  
  543. BOOL BitMap24::ReadChunkBytesCache(struct IFFHandle *iff, UBYTE *data, LONG datasize)
  544. {
  545. #define CACHESIZE 8192
  546.     static BYTE datacache[CACHESIZE];
  547.     static LONG cachesize = 0;
  548.  
  549.     if(datasize == -1)
  550.     {
  551.         cachesize = CACHESIZE;
  552.         if(ReadChunkBytes(iff,datacache,CACHESIZE) < 0)
  553.         {
  554.             error = BITMAP24_ERROR_PARSE;
  555.             return FALSE;
  556.         }
  557.     }
  558.     else
  559.     {
  560.         if(datasize > cachesize)
  561.         {
  562.             memcpy(data,datacache+CACHESIZE-cachesize,cachesize);
  563.             if(ReadChunkBytes(iff,data+cachesize,datasize-cachesize) < 0)
  564.             {
  565.                 error = BITMAP24_ERROR_PARSE;
  566.                 return FALSE;
  567.             }
  568.             if(ReadChunkBytes(iff,datacache,CACHESIZE) < 0)
  569.             {
  570.                 error = BITMAP24_ERROR_PARSE;
  571.                 return FALSE;
  572.             }
  573.  
  574.             cachesize = CACHESIZE;
  575.         }
  576.         else
  577.         {
  578.             memcpy(data,datacache+CACHESIZE-cachesize,datasize);
  579.             cachesize -= datasize;
  580.         }
  581.     }
  582.  
  583.     return TRUE;
  584. #undef CACHESIZE
  585. }
  586.  
  587.  
  588. void BitMap24::ReadBitMap(char *file)
  589. {
  590.     struct IFFHandle *iff;
  591.     BitMapHeader bmheader;
  592.     Compression compression;
  593.  
  594.     iff = OpenIFFParse(file,ID_BMHD);
  595.     if(!iff)
  596.         return;
  597.  
  598.     if(1 != ReadChunkRecords(iff, &bmheader, sizeof(bmheader), 1))
  599.     {
  600.         error = BITMAP24_ERROR_FILEFORMAT;
  601.         CloseIFFParse(iff);
  602.         return;
  603.     }
  604.  
  605.     if(bmheader.nPlanes != 24)
  606.     {
  607.         error = BITMAP24_ERROR_FILEFORMAT;
  608.         CloseIFFParse(iff);
  609.         return;
  610.     }
  611.  
  612.     SetSize(bmheader.w, bmheader.h);
  613.     if(HasError())
  614.     {
  615.         CloseIFFParse(iff);
  616.         return;
  617.     }
  618.  
  619.     compression = bmheader.compression;
  620.  
  621.     CloseIFFParse(iff);
  622.     iff = OpenIFFParse(file,ID_BODY);
  623.     if(!iff)
  624.         return;
  625.  
  626.     if(!ReadChunkBytesCache(iff,NULL,-1))
  627.     {
  628.         CloseIFFParse(iff);
  629.         return;
  630.     }
  631.     if(compression)
  632.     {
  633.         UBYTE bb[256], *bbp;
  634.         BYTE n;
  635.  
  636.         for(int row=0; row<height; row++)
  637.         {
  638.             for(int c=0; c<3; c++)
  639.             {
  640.                 for(int b=0; b<8; b++)
  641.                 {
  642.                     int x=0;
  643.                     while(x < width)
  644.                     {
  645.                         if(!ReadChunkBytesCache(iff, (UBYTE *)&n, 1))
  646.                         {
  647.                             CloseIFFParse(iff);
  648.                             return;
  649.                         }
  650.                         if(n >= 0)
  651.                         {
  652.                             if(!ReadChunkBytesCache(iff, bb, n+1))
  653.                             {
  654.                                 CloseIFFParse(iff);
  655.                                 return;
  656.                             }
  657.                             bbp = bb;
  658.                             for(BYTE xx=0; xx<=n; xx++)
  659.                             {
  660.                                 SpreadBits(row,c,b,x,*bbp);
  661.                                 bbp++;
  662.                                 x+=8;
  663.                             }
  664.                         }
  665.                         else if(n > -128)
  666.                         {
  667.                             if(!ReadChunkBytesCache(iff, bb, 1))
  668.                             {
  669.                                 CloseIFFParse(iff);
  670.                                 return;
  671.                             }
  672.                             for(BYTE xx=0; xx<(1-n); xx++)
  673.                             {
  674.                                 SpreadBits(row,c,b,x,*bb);
  675.                                 x+=8;
  676.                             }
  677.                         }
  678.                         else
  679.                         {
  680.                             ;
  681.                         }
  682.                     }
  683.                 }
  684.             }
  685.         }
  686.     }
  687.  
  688.     CloseIFFParse(iff);
  689. }
  690.  
  691.  
  692. void BitMap24::SetError(int error_arg)
  693. {
  694.     error = error_arg;
  695. }
  696.